#include <juanmabc.h>
#define JMBC_INSIDE
#include "tcGame.h"
#include "tcToy.h"

// Context

#define STORE ID_SMALL_STORE

static TCToy *store[STORE];

void
tcToyCreateContext(void)
{
	idClearStore(TCToy, STORE, store);
}

// Generation / Deletion

static
void
Init(TCToy *toy)
{
}

static
void
Term(TCToy *toy)
{
}

void
tcGenToys(unsigned int n, unsigned int *toys)
{
	idGenObjects(TCToy, STORE, store, Init, n, toys);
}

void
tcDeleteToys(unsigned int n, unsigned int *toys)
{
	idDeleteObjects(store, Term, n, toys);
}

// Load / Unload

void
tcToyLoad(unsigned int id, unsigned int name, unsigned int color, const float *position)
{
	TCGame *game;
	TCToy *toy;
	unsigned int strings[5];
	unsigned int sound;
	unsigned int i;
	const float *anchor;
	dMass mass;
	dQuaternion q1, q2;
	ALuint buffer;

	game = tcGame();
	toy = store[id];

	tlGenStrings(5, strings);

	tlStringLoad(strings[0], PACKAGE_DATADIR);

	mlGenMeshes(2, toy->meshes);

	tlStringLoad(strings[2], tlStringData(name));
	tlStringLoad(strings[3], tlStringData(color));
	tlStringConcat(strings[1], 2, &strings[2]);
	tlStringsUnload(2, &strings[2]);
	tlStringConcatPath(strings[2], 2, strings);
	tlStringLoad(strings[3], L".cmesh");
	tlStringConcat(strings[4], 2, &strings[2]);
	mlMeshLoad(toy->meshes[0], strings[4]);
	mlMeshGenMaterials(toy->meshes[0], strings[0]);
	tlStringUnload(strings[1]);
	tlStringUnload(strings[2]);
	tlStringUnload(strings[4]);
	tlStringLoad(strings[1], L"Wheel4");
	tlStringConcatPath(strings[2], 2, strings);
	tlStringConcat(strings[4], 2, &strings[2]);
	mlMeshLoad(toy->meshes[1], strings[4]);
	mlMeshGenMaterials(toy->meshes[1], strings[0]);
	tlStringsUnload(4, &strings[1]);

	toy->speed = game->options.race.speed;
	toy->steer = M_PI / 2.2f;
	toy->turbo = 0;

	dQFromAxisAndAngle(q1, 0.0f, 1.0f, 0.0f, M_PI);
	toy->bodies[0] = dBodyCreate(game->race.world);
	dMassSetBox(&mass, 0.5f, 2.5f, 1.0f, 5.0f); /// 1.0f, 2.5f, 1.0f, 5.5f
	dMassAdjust(&mass, 0.5f); // 1.of
	dBodySetQuaternion(toy->bodies[0], q1);
	dBodySetMass(toy->bodies[0], &mass);
	toy->geoms[0] = dCreateBox(game->race.space, 2.5f, 1.0f, 5.0f); // 5.5
	dGeomSetBody(toy->geoms[0], toy->bodies[0]);
	dBodySetPosition(toy->bodies[0], position[0], position[1], position[2]);

	dQFromAxisAndAngle(q2, 0.0f, 1.0f, 0.0f, - M_PI * 0.5f);
	dMassSetSphere(&mass, 0.05f, 0.5f);
	dMassAdjust(&mass, 0.05f);
	for(i = 1; i < 5; i++)
	{
		toy->bodies[i] = dBodyCreate(game->race.world);
		dBodySetQuaternion(toy->bodies[i], q2);
		dBodySetMass(toy->bodies[i], &mass);
		toy->geoms[i] = dCreateSphere(game->race.space, 0.5f);
		dGeomSetBody(toy->geoms[i], toy->bodies[i]);
	}
	dBodySetPosition(toy->bodies[1], position[0] - 1.5f, position[1] - 0.5f, position[2] + 1.5f);
	dBodySetPosition(toy->bodies[2], position[0] + 1.5f, position[1] - 0.5f, position[2] + 1.5f);
	dBodySetPosition(toy->bodies[3], position[0] - 1.5f, position[1] - 0.5f, position[2] - 1.5f);
	dBodySetPosition(toy->bodies[4], position[0] + 1.5f, position[1] - 0.5f, position[2] - 1.5f);

	for(i = 0; i < 4; i++)
	{
		toy->joints[i] = dJointCreateHinge2(game->race.world, NULL);
		dJointAttach(toy->joints[i], toy->bodies[0], toy->bodies[i + 1]);
		anchor = dBodyGetPosition(toy->bodies[i + 1]);
		dJointSetHinge2Anchor(toy->joints[i], anchor[0], anchor[1], anchor[2]);
		dJointSetHinge2Axis1(toy->joints[i], 0.0f, 1.0f, 0.0f);
		dJointSetHinge2Axis2(toy->joints[i], 1.0f, 0.0f, 0.0f);
		dJointSetHinge2Param(toy->joints[i], dParamSuspensionERP, 0.4f);
		dJointSetHinge2Param(toy->joints[i], dParamSuspensionCFM, 0.8f);
	}
	for(i = 0; i < 2; i++)
	{
		dJointSetHinge2Param(toy->joints[i], dParamLoStop, 0);
		dJointSetHinge2Param(toy->joints[i], dParamHiStop, 0);
	}

	alGenSources(1, &toy->engine);
	alGenBuffers(1, &buffer);
	slGenSounds(1, &sound);
	tlStringLoad(strings[1], L"Engine.ogg");
	tlStringConcatPath(strings[2], 2, strings);
	slSoundLoadVORBIS(sound, strings[2]);
	slSoundBufferData(sound, buffer);
	slSoundUnload(sound);
	slDeleteSounds(1, &sound);
	alSourcei(toy->engine, AL_BUFFER, buffer);
	alSourcei(toy->engine, AL_LOOPING, AL_TRUE);
	alSource3f(toy->engine, AL_POSITION, position[0], position[1], position[2]);
	alSourcef(toy->engine, AL_ROLLOFF_FACTOR, 0.1f);
	tlStringsUnload(3, strings);

	tlDeleteStrings(5, strings);
}

void
tcToyUnload(unsigned int id)
{
	TCToy *toy;
	ALuint buffer;
	unsigned int i;

	toy = store[id];

	alGetSourcei(toy->engine, AL_BUFFER, (int *) &buffer);
	alDeleteSources(1, &toy->engine);
	alDeleteBuffers(1, &buffer);

	mlMeshDeleteMaterials(toy->meshes[0]);
	mlMeshDeleteMaterials(toy->meshes[1]);
	mlMeshesUnload(2, toy->meshes);
	mlDeleteMeshes(2, toy->meshes);

	for(i = 0; i < 4; i++)
		dJointDestroy(toy->joints[i]);

	for(i = 0; i < 5; i++)
	{
		dGeomDestroy(toy->geoms[i]);
		dBodyDestroy(toy->bodies[i]);
	}
}

void
tcToysUnload(unsigned int n, unsigned int *toys)
{
	unsigned int i;

	for(i = 0; i < n; i++)
		tcToyUnload(toys[i]);
}

// Query


void
tcToyFrame(unsigned int id)
{
	TCToy *toy;
	unsigned int i;
	const float *p, *r;
	float m[16], v;

	toy = store[id];

	for(v = 0, i = 0; i < 4; v += mlVectorNorm(dBodyGetAngularVel(toy->bodies[i + 1])), i++);
	alSourcef(toy->engine, AL_PITCH, mlClamp(v / 80.0f, 0.0f, 2.00f) + 1.00f); // 80.0f, 0.0f, 1.5f, 0.5f

	p = dBodyGetPosition(toy->bodies[0]);
	alSource3f(toy->engine, AL_POSITION, p[0], p[1], p[2]);

	p = dBodyGetLinearVel(toy->bodies[0]);
	alSource3f(toy->engine, AL_VELOCITY, p[0], p[1], p[2]);

	//r = dBodyGetRotation(toy->bodies[0]);
	//alSource3f(toy->engine, AL_DIRECTION, r[2], r[6], r[10]);

	for(i = 0; i < 5; i++)
	{
		p = dBodyGetPosition(toy->bodies[i]);
        	r = dBodyGetRotation(toy->bodies[i]);
		mlMatrixLoadPositionTransposedRotation(m, p, r);
		glPushMatrix();
		glMultMatrixf(m);
		mlMeshDraw((i ? toy->meshes[1] : toy->meshes[0]));
		glPopMatrix();
	}
}

void
tcToyEnable(unsigned int id)
{
	TCToy *toy;
	unsigned int i;

	toy = store[id];

	for(i = 0; i < 5; i++)
	{
		dBodyEnable(toy->bodies[i]);
		dGeomEnable(toy->geoms[i]);
	}

	alSourcePlay(toy->engine);
}

void
tcToyDisable(unsigned int id)
{
	TCToy *toy;
	unsigned int i;

	toy = store[id];

	alSourceStop(toy->engine);

	for(i = 0; i < 5; i++)
	{
		dGeomDisable(toy->geoms[i]);
		dBodyDisable(toy->bodies[i]);
	}
}

int
tcToyIsEnabled(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	return dBodyIsEnabled(toy->bodies[0]);
}

void
tcToyPause(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	alSourcePause(toy->engine);
}

void
tcToyPlay(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	alSourcePlay(toy->engine);
}

const float *
tcToyGetPosition(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	return dBodyGetPosition(toy->bodies[0]);
}

const float *
tcToyGetRotation(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	return dBodyGetRotation(toy->bodies[0]);
}

const float *
tcToyGetLinearVel(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	return dBodyGetLinearVel(toy->bodies[0]);
}

void
tcToyDrive(unsigned int id, int up, int down, int left, int right)
{
	TCToy *toy;
	float speed, steer;
	unsigned int i;

	toy = store[id];

	speed = toy->speed * (toy->turbo ? 2.0f : 1.0f) * (up ? 1.0f : (down ? -1.0f : 0.0f));
	steer = toy->steer * (left ? -1.0f : (right ? 1.0f : 0.0f));

	if(steer)
	{
		for(i = 2; i < 4; i++)
		{
			dJointSetHinge2Param(toy->joints[i], dParamVel, steer);
			dJointSetHinge2Param(toy->joints[i], dParamFMax, 20.0f); // 4.0f
			dJointSetHinge2Param(toy->joints[i], dParamLoStop, -1.00f); // -0.75f
			dJointSetHinge2Param(toy->joints[i], dParamHiStop, 1.00f); // 0.75f
			dJointSetHinge2Param(toy->joints[i], dParamFudgeFactor, 0.01f); // 0.1f
		}
	}
	else
	{
		for(i = 2; i < 4; i++)
		{
			dJointSetHinge2Param(toy->joints[i], dParamLoStop, 0.0f);
			dJointSetHinge2Param(toy->joints[i], dParamHiStop, 0.0f);
			dJointSetHinge2Param(toy->joints[i], dParamFudgeFactor, 0.01f); // 0.1f
		}
	}
	for(i = 0; i < 4; i++)
	{
		dJointSetHinge2Param(toy->joints[i], dParamVel2, speed);
		dJointSetHinge2Param(toy->joints[i], dParamFMax2, 20.0f); // 2.0f
	}
	for(i = 0; i < 2; i++)
	{
		dJointSetHinge2Param(toy->joints[i], dParamLoStop, 0.0f);
		dJointSetHinge2Param(toy->joints[i], dParamHiStop, 0.0f);
		dJointSetHinge2Param(toy->joints[i], dParamFudgeFactor, 0.01f);
	}
}

void
tcToyTurbo(unsigned int id)
{
	TCToy *toy;
	
	toy = store[id];

	toy->turbo = 1;
}

void
tcToyUnturbo(unsigned int id)
{
	TCToy *toy;

	toy = store[id];

	toy->turbo = 0;
}

void
tcToyJump(unsigned int id)
{
	TCToy *toy;
	unsigned int i;
	
	toy = store[id];

	for(i = 0; i < 5; i++)
		dBodyAddForce(toy->bodies[i], 0.0f, 75.0f, 0.0f);
}

void
tcToyPlace(unsigned int id, const float *position, const float *rotation)
{
	float matrix[12], srotation[16], wposition[3];
	TCToy *toy;

	toy = store[id];

	mlMatrixRotateTo(rotation, mlVectorY, 180.0f, srotation);
	mlMatrixLoadTransposedRotation(matrix, srotation);

	dBodySetPosition(toy->bodies[0], position[0], position[1], position[2]);
	dBodySetRotation(toy->bodies[0], matrix);
	dBodySetLinearVel(toy->bodies[0], 0.0f, 0.0f, 0.0f);
	dBodySetAngularVel(toy->bodies[0], 0.0f, 0.0f, 0.0f);

	mlMatrixRotateTo(rotation, mlVectorY, -90.0f, srotation);
	mlMatrixLoadTransposedRotation(matrix, srotation);

	mlVectorLoad(wposition, -1.5f, -0.5f, 1.25f);
	mlVectorMultMatrix(wposition, rotation);
	mlVectorAdd(wposition, position);
	dBodySetPosition(toy->bodies[1], wposition[0], wposition[1], wposition[2]);
	dBodySetRotation(toy->bodies[1], matrix);
	dBodySetLinearVel(toy->bodies[1], 0.0f, 0.0f, 0.0f);
	dBodySetAngularVel(toy->bodies[1], 0.0f, 0.0f, 0.0f);

	mlVectorLoad(wposition, 1.5f, -0.5f, 1.25f);
	mlVectorMultMatrix(wposition, rotation);
	mlVectorAdd(wposition, position);
	dBodySetPosition(toy->bodies[2], wposition[0], wposition[1], wposition[2]);
	dBodySetRotation(toy->bodies[2], matrix);
	dBodySetLinearVel(toy->bodies[2], 0.0f, 0.0f, 0.0f);
	dBodySetAngularVel(toy->bodies[2], 0.0f, 0.0f, 0.0f);

	mlVectorLoad(wposition, -1.5f, -0.5f, -1.25f);
	mlVectorMultMatrix(wposition, rotation);
	mlVectorAdd(wposition, position);
	dBodySetPosition(toy->bodies[3], wposition[0], wposition[1], wposition[2]);
	dBodySetRotation(toy->bodies[3], matrix);
	dBodySetLinearVel(toy->bodies[3], 0.0f, 0.0f, 0.0f);
	dBodySetAngularVel(toy->bodies[3], 0.0f, 0.0f, 0.0f);

	mlVectorLoad(wposition, 1.5f, -0.5f, -1.25f);
	mlVectorMultMatrix(wposition, rotation);
	mlVectorAdd(wposition, position);
	dBodySetPosition(toy->bodies[4], wposition[0], wposition[1], wposition[2]);
	dBodySetRotation(toy->bodies[4], matrix);
	dBodySetLinearVel(toy->bodies[4], 0.0f, 0.0f, 0.0f);
	dBodySetAngularVel(toy->bodies[4], 0.0f, 0.0f, 0.0f);
}
